package com.xysoft.framework.orm;

import java.util.Iterator;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.internal.CriteriaImpl;

import com.xysoft.framework.entity.AbstractPagedCollection;

public class HibernatePagedCollection<T> extends AbstractPagedCollection<T> {

	private Integer cachedRecordCount;
	private List<T> cachedListResult;
	protected CriteriaImpl criteria;

	public HibernatePagedCollection(Criteria criteria) {
		this.criteria = (CriteriaImpl) criteria;
	}

	public CriteriaImpl getCriteria() {
		return criteria;
	}

	@SuppressWarnings("unchecked")
	@Override
	public Iterator<T> iterator() {
		if (cachedListResult == null) {
			synchronized (this) {
				int firstResult = getPageNo() * getPageSize();
				int maxResults = getPageSize();
				if (maxResults > 1 && (firstResult + maxResults) > getTotalCount()) {
					maxResults = getTotalCount() - firstResult;
				}

				getCriteria().setResultTransformer(CriteriaSpecification.ROOT_ENTITY);

				getCriteria().setFirstResult(firstResult);
				getCriteria().setMaxResults(maxResults);

				cachedListResult = getCriteria().list();
			}
		}
		return cachedListResult.iterator();
	}

	@Override
	public int getTotalCount() {
		if (cachedRecordCount == null) {
			synchronized (this) {
				Projection prevProjection = getCriteria().getProjection();

				getCriteria().setProjection(Projections.rowCount());
				getCriteria().setResultTransformer(CriteriaSpecification.PROJECTION);

				try {
					cachedRecordCount = Integer.valueOf(getCriteria().uniqueResult().toString());
				} finally {
					getCriteria().setProjection(prevProjection);
				}
			}
		}
		return cachedRecordCount;
	}

}
